﻿using System.Text;
using SqlSugar;
using Digitalmes.Common.Utils;
using Digitalmes.Generator.Utils;
using Digitalmes.Sugar;
using Digitalmes.Domain;

namespace Digitalmes.Generator;

/// <summary>
/// 代码生成实体对象
/// </summary>
public sealed class GeneratorEntity : EntityBaseId
{
}

public sealed class GeneratorService : SugarRepository<GeneratorEntity>, IGeneratorService
{
    /// <summary>
    /// 生成代码
    /// </summary>
    /// <returns></returns>
    public string CreateCode(GeneratorTableDto createModel)
    {
        var templatePath = Path.Combine(AppUtils.RootPath, "template");

        // 读取模板——实体
        var modelTemp = FileUtils.ReadTextIgnoreExists(Path.Combine(templatePath, "Model.txt"));
        // 读取模板——Dto
        var dtoTemp = FileUtils.ReadTextIgnoreExists(Path.Combine(templatePath, "Dto.txt"));
        // 读取模板——服务实现
        var serviceTemp = FileUtils.ReadTextIgnoreExists(Path.Combine(templatePath, "Service.txt"));
        // 读取模板Tree——服务实现
        var treeServiceTemp = FileUtils.ReadTextIgnoreExists(Path.Combine(templatePath, "TreeService.txt"));

        // 读取模板Js——前端
        var jsTemp = FileUtils.ReadTextIgnoreExists(Path.Combine(templatePath, "Vue", "Js.txt"));
        // 读取模板List——前端
        var listTemp = FileUtils.ReadTextIgnoreExists(Path.Combine(templatePath, "Vue", "List.txt"));
        // 读取模板Modify——前端
        var modifyTemp = FileUtils.ReadTextIgnoreExists(Path.Combine(templatePath, "Vue", "Modify.txt"));


        // 判断是否存在树形结构的实体，如果存在，则使用Tree模板
        bool isParent = false, isParentList = false, isLayer = false;
        // 构建属性
        StringBuilder attrStr = new(), dtoAttrStr = new();
        string tableColumn = "";
        foreach (var item in createModel.TableColumnInfo!)
        {
            if (EntityConstant.IgnoreDtoColumn.Contains(item.DbColumnName))
            {
                continue;
            }

            switch (item.DbColumnName)
            {
                case "ParentId":
                    isParent = true;
                    break;
                case "ParentIdList":
                    isParentList = true;
                    break;
                case "Layer":
                    isLayer = true;
                    break;
            }

            if (!item.IsPrimarykey)
            {
                attrStr.AppendLine("    /// <summary>");
                attrStr.AppendLine($"    /// {item.ColumnDescription}");
                attrStr.AppendLine("    /// </summary>");


                if (!item.IsNullable && item.DataType.ConvertModelType() == "string")
                {
                    attrStr.AppendLine("    [Required, NotNull]");
                    attrStr.AppendLine($"    [StringLength({item.Length})]");
                }

                attrStr.AppendLine($"    public {item.DataType.ConvertModelType(item.IsNullable)} {item.DbColumnName} {{ get; set; }} {item.DataType.ModelDefaultValue(item.DefaultValue, item.IsNullable)}");
                attrStr.AppendLine();
            }

            dtoAttrStr.AppendLine("    /// <summary>");
            dtoAttrStr.AppendLine($"    /// {item.ColumnDescription}");
            dtoAttrStr.AppendLine("    /// </summary>");
            if (!item.IsNullable && item.DataType.ConvertModelType() == "string")
            {
                dtoAttrStr.AppendLine("    [Required, NotNull]");
                dtoAttrStr.AppendLine($"    [StringLength({item.Length})]");
            }

            dtoAttrStr.AppendLine($"    public {item.DataType.ConvertModelType(item.IsNullable)} {item.DbColumnName} {{ get; set; }} {item.DataType.ModelDefaultValue(item.DefaultValue, item.IsNullable)}");
            dtoAttrStr.AppendLine();
        }

        var outpath = Path.Combine(AppUtils.RootPath, "output", "Generator");
        FileUtils.CreateDirectoryIfNotExists(outpath);

        var table = Context.DbMaintenance.GetTableInfoList().First(m => createModel.TableNames.Contains(m.Name));
        var modelName = table.Name.TableName();
        tableColumn = modelTemp
            .Replace("{NameSpace}", createModel.Namespace)
            .Replace("{TableNameDescribe}", table.Description)
            .Replace("{DataTable}", table.Name)
            .Replace("{TableName}", modelName)
            .Replace("{AttributeList}", attrStr.ToString());

        // 写入文件-Model
        FileUtils.CreateDirAndWriteText(Path.Combine(outpath, "Model", createModel.Namespace ?? ""), modelName + ".cs", tableColumn);

        var serverPath = Path.Combine(outpath, createModel.Namespace ?? "", modelName + "Service");

        // Dto
        string dtoString = dtoTemp.Replace("{NameSpace}", createModel.Namespace)
            .Replace("{TableNameDescribe}", table.Description.Replace("\r\n", "/"))
            .Replace("{TableName}", modelName)
            .Replace("{AttributeList}", dtoAttrStr.ToString());
        FileUtils.CreateDirAndWriteText(Path.Combine(serverPath, "Dto"), modelName + "Dto.cs", dtoString);

        if (!isParent && !isParentList && !isLayer)
        {
            //接口实现
            string serviceString = serviceTemp.Replace("{NameSpace}", createModel.Namespace)
                .Replace("{TableNameDescribe}", table.Description.Replace("\r\n", "/"))
                .Replace("{Version}", createModel.ApiVersion)
                .Replace("{TableName}", modelName);
            FileUtils.CreateDirAndWriteText(serverPath, modelName + "Service.cs", serviceString);
        }
        else
        {
            //接口实现
            string treeServiceString = treeServiceTemp.Replace("{NameSpace}", createModel.Namespace)
                .Replace("{TableNameDescribe}", table.Description.Replace("\r\n", "/"))
                .Replace("{Version}", createModel.ApiVersion)
                .Replace("{TableName}", modelName);
            FileUtils.CreateDirAndWriteText(serverPath, modelName + "Service.cs", treeServiceString);
        }

        #region 前端

        //JS
        var jsString = jsTemp.Replace("{TableName}", modelName.ToLower());
        FileUtils.CreateDirAndWriteText(Path.Combine(outpath, "Vue"), modelName.ToLower() + ".js", jsString);

        //List
        string webTableColumnStr = string.Empty, formData = string.Empty, rules = string.Empty;
        StringBuilder formColumnStr = new(), optionsString = new();

        if (createModel.IsGrid)
        {
            formColumnStr.Append("<el-row>");
        }

        foreach (var item in createModel.TableColumnInfo)
        {
            //列
            if (item.IsColumn)
            {
                webTableColumnStr += "                { prop: '" + item.DbColumnName.FirstCharToLower() + "', label: '" + item.ColumnDescription + "', width: 100 },\r\n";
            }

            if (!item.IsAdd) 
                continue;

            if (createModel.IsGrid)
            {
                formColumnStr.AppendLine("<el-col :span=\"12\">");
            }
            if (item.ComponentType is "input" or "textarea")
            {
                formColumnStr.AppendLine($"\t\t\t<el-form-item label=\"{item.ColumnDescription}\" prop=\"{item.DbColumnName.FirstCharToLower()}\">");
                formColumnStr.AppendLine("\t\t\t	<el-input");
                formColumnStr.AppendLine($"\t\t\t		v-model=\"formData.{item.DbColumnName.FirstCharToLower()}\"");
                if (item.ComponentType == "textarea")
                {
                    formColumnStr.AppendLine("\t\t\t		type=\"textarea\"");
                }
                formColumnStr.AppendLine($"\t\t\t		placeholder=\"请输入{item.ColumnDescription}\"");
                formColumnStr.AppendLine($"\t\t\t		:maxlength=\"{item.Length}\"");
                formColumnStr.AppendLine("\t\t\t		show-word-limit");
                formColumnStr.AppendLine("\t\t\t		clearable>");
                formColumnStr.AppendLine("\t\t\t	</el-input>");
                formColumnStr.AppendLine("\t\t\t</el-form-item>");
            }

            if (item.ComponentType == "switch")
            {
                formColumnStr.AppendLine($"\t\t\t<el-form-item label=\"{item.ColumnDescription}\" prop=\"{item.DbColumnName.FirstCharToLower()}\">");
                formColumnStr.AppendLine($"\t\t\t	<el-switch v-model=\"formData.{item.DbColumnName.FirstCharToLower()}\"></el-switch>");
                formColumnStr.AppendLine("\t\t\t</el-form-item>)");
            }
            if (item.ComponentType == "time")
            {
                formColumnStr.AppendLine($"\t\t\t<el-form-item label=\"{item.ColumnDescription}\" prop=\"{item.DbColumnName.FirstCharToLower()}\">");
                formColumnStr.AppendLine("\t\t\t	<el-date-picker :style=\"{ width: '100%' }\" clearable ");
                formColumnStr.AppendLine("\t\t\t		v-model=\"formData.{item.DbColumnName.FirstCharToLower()}\"");
                formColumnStr.AppendLine($"\t\t\t	    placeholder=\"请输入{item.ColumnDescription}\">");
                formColumnStr.AppendLine("\t\t\t</el-date-picker>");
                formColumnStr.AppendLine("\t\t\t</el-form-item>");
            }
            if (item.ComponentType == "select")
            {
                formColumnStr.AppendLine($"\t\t\t<el-form-item label=\"{item.ColumnDescription}\" prop=\"{item.DbColumnName.FirstCharToLower()}\">");
                formColumnStr.AppendLine("\t\t\t	<el-select clearable");
                formColumnStr.AppendLine($"\t\t\t		v-model=\"formData.{item.DbColumnName.FirstCharToLower()}\"");
                formColumnStr.AppendLine($"\t\t\t	    placeholder=\"请选择{item.ColumnDescription}\">");
                formColumnStr.AppendLine("\t\t\t	<el-option");
                formColumnStr.AppendLine("\t\t\t	    v-for=\"item in {item.DbColumnName.FirstCharToLower()}Options\"");
                formColumnStr.AppendLine("\t\t\t	    :key=\"item.value\"");
                formColumnStr.AppendLine("\t\t\t	    :label=\"item.label\"");
                formColumnStr.AppendLine("\t\t\t	    :value=\"item.value\" />");
                formColumnStr.AppendLine("\t\t\t</el-select>");
                formColumnStr.AppendLine("\t\t\t</el-form-item>");

                optionsString.Append(item.DbColumnName.FirstCharToLower() + "Options:[],");
            }

            if (createModel.IsGrid)
            {
                formColumnStr.AppendLine("</el-col>");
            }

            formData += $"\t\t\t\t{item.DbColumnName.FirstCharToLower()}: {(item.ComponentType == "switch" ? "true" : "undefined")},\r\n";
            if (!item.IsNullable)
            {
                rules += $"\t\t\t\t{item.DbColumnName.FirstCharToLower()}: [{{ required: true, message: \"请输入{item.ColumnDescription}\", trigger: \"{ (item.ComponentType is "select" or "time" ? "change" : "blur")}\" }}],\r\n";
            }

        }
        if (createModel.IsGrid)
        {
            formColumnStr.AppendLine("</el-row>");
        }

        var listString = listTemp.Replace("{TableName}", modelName.ToLower())
            .Replace("{NameSpace}", createModel.Namespace!.ToLower())
            .Replace("{Column}", webTableColumnStr);
        FileUtils.CreateDirAndWriteText(Path.Combine(outpath, "Vue", createModel.Namespace,  modelName), "index.vue", listString);


        // Modify
        var modifyString = modifyTemp.Replace("{TableName}", modelName.ToLower())
            .Replace("{NameSpace}", createModel.Namespace.ToLower())
            .Replace("{TableColumn}", formColumnStr.ToString())
            .Replace("{formData}", formData)
            .Replace("[RULES]", rules)
            .Replace("{options}", optionsString.ToString())
            .Replace("{TableNameDescribe}", table.Description);
        FileUtils.CreateDirAndWriteText(Path.Combine(outpath, "Vue", createModel.Namespace, modelName), "modify.vue", modifyString);

        #endregion

        return outpath;
    }

    /// <summary>
    /// 连接数据库，并返回当前连接下所有表名字
    /// </summary>
    /// <returns></returns>
    public List<DbTableInfo> InitTable()
    {
        return Context.DbMaintenance.GetTableInfoList();
    }

    /// <summary>
    /// 根据表名查询列信息
    /// </summary>
    /// <param name="tableName"></param>
    /// <returns></returns>
    public List<DbColumnInfo> GetColumn(string tableName)
    {
        return Context.DbMaintenance.GetColumnInfosByTableName(tableName);
    }
}
